Spring AOP 属于第二代 AOP, 采用动态代理机制和字节码生成技术实现 。
与最初的 AspectJ 采用编译器将横切逻辑织入目标对象不同,动态代理机制和字节码生成都是在运行期间为目标对象生成一个代理对象,而将横切逻辑织入到这个代理对象中,系统最终使用的是织入了横切逻辑的代理对象,而不是真正的目标对象。
一、动态代理
我们可以为指定的接口在系统运行期间动态的生成代理对象, 从而帮助我们走出最初使用静态代理实现 AOP 的窘境
动态代理机制的实现主要由 java.lang.reflect.Proxy 类和java.lang.reflect.InvocationHandler接口。
详细使用请看:Java设计模式之—静态代理和动态代理
动态代理的缺点:动态代理只能对实现了相应Interface的类使用,如果某个类没有实现任何的Interface,就无法使用动态代理对其产生相应的代理对象!
因此:在默认情况下,如果Spring AOP发现目标实现了相应的Interface,则采用动态代理为其生成代理对象实例;而如果目标对象没有实现任何的Interface,Spring AOP会尝试使用CGLIB动态字节码生成类库,为目标对象生成代理对象实例!
二、CGLIB动态字节码生成
使用CGLIB扩展对象行为的原理是:对目标对象进行继承扩展,为其生成相应的子类,而子类可以通过覆写来扩展父类的行为,只要将横切逻辑的实现放到子类中,然后让系统使用扩展后的目标对象的子类,就可以达到与代理模式相同的效果了。
记住:相比于动态代理,CGLIB的优势就是,可以为没有实现任何接口的类进行扩展
例如:
目标类如下:(这里不实现任何接口)
public class Requestable {
public void request() {
System.out.println("requestable without implementint any interface");
}
}
要对该类进行继承扩展,所以我们要使用CGLIB类库,实现一个net.sf.cglib.proxy.Callback,或者使用net.sf.cglib.proxy.MethodInterceptor(继承自Callback)
import java.lang.reflect.Method;
import org.joda.time.TimeOfDay;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class RequestCallback implements MethodInterceptor {
public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if(method.getName().equals("request")) {
System.out.println("你调用了request方法")
return proxy.invokeSuper(object, args);
}
return null;
}
}
到了这里,我们使用MethodInterceptor为request方法实现了对request请求进行控制的逻辑
然后我们现在通过Enhancer为目标对象动态生成一个子类,将RequestCallback的横切逻辑附加到该子类中:
import net.sf.cglib.proxy.Enhancer;
public class Client {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(Requestable.class);
//设置Callback
enhancer.setCallback(new RequestCallback());
Requestable proxy = (Requestable) enhancer.create();
proxy.request();
}
}
结果如下:
你调用了request方法
requestable without implementint any interface
缺点:使用 CGLIB 对类进行扩展的唯一限制就是 无法对 final 方法进行覆写
三、总结
以上两种技术(动态代理 与 动态字节码生成) 就是 Spring AOP 所使用的核心技术,也就是 Spring AOP 的Weaving And Weaver 的实现原理了
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。